package com.hebeiwangxiaozhang.admin.service;

import com.hebeiwangxiaozhang.admin.pojo.*;
import com.hebeiwangxiaozhang.admin.repo.CommodityBrandRepository;
import com.hebeiwangxiaozhang.admin.repo.CommodityCategoryBrandRepository;
import com.hebeiwangxiaozhang.admin.repo.CommodityCategoryRepository;
import com.hebeiwangxiaozhang.admin.utils.RedisCommonProcessor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CommodityCategoryService {

    @Autowired
    private RedisCommonProcessor redisCommonProcessor;

    @Autowired
    private CommodityCategoryRepository commodityCategoryRepository;

    @Autowired
    private CommodityCategoryBrandRepository commodityCategoryBrandRepository;

    @Autowired
    private CommodityBrandRepository commodityBrandRepository;

    @Autowired
    private RedissonClient redissonClient;

    public CommodityCategoryComposite getCategoryList() {
        Object cacheItems = redisCommonProcessor.get("items");
        if(cacheItems != null) {
            return (CommodityCategoryComposite) cacheItems;
        }
        RLock itemsLock = redissonClient.getLock("itemsLock");
        itemsLock.lock(30, TimeUnit.SECONDS);
        try {
            List<CommodityCategory> categoryList = commodityCategoryRepository.findAll();
            CommodityCategoryComposite items = generateProductTree(categoryList);
            if(items == null) {
                throw new UnsupportedOperationException("Product items should not be empty in DB!");
            }
            redisCommonProcessor.set("items", items);
            return items;
        } catch (Exception e) {
            throw new UnsupportedOperationException(e);
        } finally {
            itemsLock.unlock();
        }
    }

    private CommodityCategoryComposite generateProductTree(List<CommodityCategory> categoryList) {
        List<CommodityCategoryComposite> composites = new ArrayList<>(categoryList.size());
        categoryList.forEach(dbItem -> composites.add(CommodityCategoryComposite.builder()
                .id(dbItem.getId())
                .name(dbItem.getName())
                .parentId(dbItem.getParentId())
                .build()));
        Map<Long, List<CommodityCategoryComposite>> groupingList =
                composites.stream().collect(Collectors.groupingBy(CommodityCategoryComposite::getParentId));
        composites.stream().forEach(item -> {
            List<CommodityCategoryComposite> list = groupingList.get(item.getId());
            item.setChild(list == null ? new ArrayList<>() : list);
        });
        CommodityCategoryComposite composite = composites.size() == 0 ? null : composites.get(0);
        return composite;
    }

    @Transactional
    public void create(CommodityCategory category) {
        Date date = new Date();
        category.setCreateTime(date);
        category.setUpdateTime(date);
        commodityCategoryRepository.save(category);
        redisCommonProcessor.remove("items");
    }

    @Transactional
    public void delete(Long id) {
        commodityCategoryRepository.deleteById(id);
        commodityCategoryBrandRepository.deleteByCategoryId(id);
        redisCommonProcessor.remove("items");
    }

    public void update(CommodityCategory category) {
        CommodityCategory commodityCategory = commodityCategoryRepository.getById(category.getId());
        category.setCreateTime(commodityCategory.getCreateTime());
        category.setUpdateTime(new Date());
        commodityCategoryRepository.save(category);
        redisCommonProcessor.remove("items");
    }

    @Transactional
    public void createBrandRelation(Long brandId, Long categoryId) {
        Optional<CommodityCategory> categoryOptional = commodityCategoryRepository.findById(categoryId);
        if (!categoryOptional.isPresent()) {
            throw new RuntimeException("categoryId not found");
        }

        Optional<CommodityBrand> brandOptional = commodityBrandRepository.findById(brandId);
        if (!brandOptional.isPresent()) {
            throw new RuntimeException("brandId not found");
        }

        CommodityCategoryBrand commodityCategoryBrand = CommodityCategoryBrand.builder()
                .brandId(brandId)
                .categoryId(categoryId)
                .createTime(new Date())
                .build();
        commodityCategoryBrandRepository.save(commodityCategoryBrand);
    }

    public void deleteBrandRelation(Long brandId, Long categoryId) {
        CommodityCategoryBrandPK categoryBrandPK = CommodityCategoryBrandPK.builder()
                .brandId(brandId)
                .categoryId(categoryId)
                .build();
        commodityCategoryBrandRepository.deleteById(categoryBrandPK);
    }

}
